package cn.com.sbell.vss.csg.vsclient.util;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.Collections;

import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpHost;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import cn.com.sbell.vss.csg.vap.bean.AlarmMessage;

/**
 * @author liang.a.chen
 */
public class AlarmDispatchGroup {
	
	private LinkedHashMap alarmList;
	private LinkedHashMap alarmHistory;
	private DispatchThread[] workThread;
	private HttpClient httpClient;
	private HostConfiguration hostConfiguration; 
	private volatile boolean isDestroy;
	private  String asURL;
	private final static Log logger = LogFactory.getLog(AlarmDispatchGroup.class);
	
	private static AlarmDispatchGroup dispatchGroup;
	
	static{
		dispatchGroup = new AlarmDispatchGroup();
		dispatchGroup.init(CSGConfiguration.COMMON_ALARM_DISPATCH_THREAD_NUM);
	}
	
	private AlarmDispatchGroup(){
		alarmList = new LinkedHashMap();
		alarmHistory = new LinkedHashMap(){
			protected boolean removeEldestEntry(Map.Entry eldest) {
				return System.currentTimeMillis() - ((Long)eldest.getValue()).longValue()>= CSGConfiguration.COMMON_ALARM_DISPATCH_HOLDTIME * 1000;
		    }
		};
//		hostConfiguration = new HostConfiguration();
//		hostConfiguration.setHost(new HttpHost(CSGConfiguration.AS_IPADDRESS, CSGConfiguration.AS_PORT));
	}
	
	private void init(int poolSize){
		workThread = new DispatchThread[poolSize];
		for(int i=0;i<poolSize;i++){
			workThread[i] = new DispatchThread(i);
			workThread[i].setDaemon(true);
			workThread[i].setPriority(Thread.MIN_PRIORITY);
			workThread[i].start();
		}
	}
	public static AlarmDispatchGroup getAlarmDispatchGroup(){
		return dispatchGroup;
	}

	
	public HttpClient getHttpClient() {
		return httpClient;
	}

	public void setHttpClient(HttpClient httpClient) {
		this.httpClient = httpClient;
	}

	public synchronized boolean addMessage(AlarmMessage msg_1){
		if(isDestroy){
			logger.debug("Dispatch Group is already closed!");
			return false;
		}
		AlarmMessage msg=(AlarmMessage)msg_1.clone();
		logger.debug("Alarm Received:[Type:" + msg.getAlarmType() + ", IP:" + msg.getServerIp() + "]");
		if(alarmList.containsKey(msg)){
			logger.debug("Alarm Skipped due to same one in queue!");
			return false;
		}
		Long timestamp = new Long(System.currentTimeMillis());
//		added vondart
//		alarmList.remove(msg);
		alarmList.put(msg, timestamp);
		this.notify();
		return true;
	}
	
	public synchronized Map.Entry removeMessage(){
		Map.Entry entry = null;
		Iterator iterator = alarmList.entrySet().iterator();
		if(iterator.hasNext()){
			entry = (Map.Entry)iterator.next();
			iterator.remove();
		}
		logger.debug("list size: "+alarmList.size());
		return entry;
	}
	
	private void cacheAlarmHistory(AlarmMessage msg){
		synchronized(alarmHistory){
//			added vondart
			alarmHistory.remove(msg);
			alarmHistory.put(msg, new Long(System.currentTimeMillis()));
		}
	}
	
	private boolean inRecentHistory(AlarmMessage msg){
		Long sendOutTime = null;
		synchronized(alarmHistory){
			sendOutTime = (Long)alarmHistory.get(msg);
		}
		if(sendOutTime != null && System.currentTimeMillis() - sendOutTime.longValue() < CSGConfiguration.COMMON_ALARM_DISPATCH_HOLDTIME * 1000 ){
			return true;
		}
		return false;
	}
	
	public synchronized boolean isEmpty(){
		return alarmList.isEmpty();
	}
	
	public synchronized void destroy(){
		isDestroy = true;
		this.notifyAll();
	}
	private class DispatchThread extends Thread{
		
		public DispatchThread(int id){
			super("Dispatch Thread - " + id);
		}
		
		public void run(){
			while(true){
				Map.Entry entry = null;
				synchronized(dispatchGroup){
					if(dispatchGroup.isEmpty()){
						if(isDestroy){
							break;
						}
						try {
							logger.debug(this + " is suspended!");
							dispatchGroup.wait();
							if(logger!=null&&this!=null){
							logger.debug(this + " is wake up!");
							}
						} catch (InterruptedException e) {
							logger.error(e);
						}
					}
					entry = dispatchGroup.removeMessage();
				}
				if(entry == null){
					continue;
				}
				AlarmMessage msg = (AlarmMessage)entry.getKey();
				Long timestamp = (Long)entry.getValue();
				if(inRecentHistory(msg)){
					logger.info("Alarm Skipped: [TS:" + timestamp + ", Type:" + msg.getAlarmType() + ", IP:" + msg.getServerIp() + "]");
					continue;
				}
				if(reportAlarm(msg, timestamp)){
					logger.info("Alarm SendOut: [TS:" + timestamp + ", Type:" + msg.getAlarmType() + ", IP:" + msg.getServerIp() + "]");
				}
			}
			logger.info(this + " is exited!");
		}
		
		public boolean reportAlarm(AlarmMessage msg, Long timestamp){
			boolean result = true;
			cacheAlarmHistory(msg);
			
			PostMethod method = new PostMethod(asURL+"/RaiseAlarm");
			
			//add parameters to request
			addParametersToRequest(method,msg,timestamp);
			
			logger.info("Send alarm to: "+asURL);
			
			//record alarm message
			logger.debug("AlarmMessage is:\n"+msg.toString());
			
			try {
				httpClient.executeMethod(method);
				method.getResponseBodyAsString();
			} catch (IOException e) {
				result = false;
				logger.warn("Alarm Send-Failed:" + e);
			} finally{
				method.releaseConnection();
			}
			return result;
		}

		private void addParametersToRequest(PostMethod method, AlarmMessage msg, Long timestamp) {
			ArrayList validParameterList=new ArrayList();
			if(null!=msg.getAlarmType())
			{
				validParameterList.add(new NameValuePair("alarmType", msg.getAlarmType()));
			}
			if(null!=msg.getDeviceId())
			{
				validParameterList.add(new NameValuePair("deviceId", msg.getDeviceId()));
			}
			if(null!=msg.getServerIp())
			{
				validParameterList.add(new NameValuePair("serverIp", msg.getServerIp()));
			}
			if(null!=msg.getServerType())
			{
				validParameterList.add(new NameValuePair("serverType", msg.getServerType()));
			}
			if(null!=msg.getChannelNumber())
			{
				validParameterList.add(new NameValuePair("channelNumber", msg.getChannelNumber().toString()));
			}
			if(null!=msg.getCameraNumber())
			{
				validParameterList.add(new NameValuePair("cameraNumber", msg.getCameraNumber().toString()));
			}
			if(null!=msg.getDiskNumber())
			{
				validParameterList.add(new NameValuePair("diskNumber", msg.getDiskNumber().toString()));
			}
			if(null!=msg.getData())
			{
				validParameterList.add(new NameValuePair("data", msg.getData()));
			}
			if(null!=timestamp)
			{
				validParameterList.add(new NameValuePair("timestamp", String.valueOf(timestamp.longValue()/1000)));
			}
			if(null!=msg.getAlarmCode())
			{
				validParameterList.add(new NameValuePair("alarmCode", msg.getAlarmCode()));
			}
			if(null!=msg.getEventType())
			{
				validParameterList.add(new NameValuePair("eventType", String.valueOf(msg.getEventType().intValue())));
			}
			validParameterList.add(new NameValuePair("eliminated", String.valueOf(msg.isEliminated())));
			NameValuePair[] parameters=(NameValuePair[]) validParameterList.toArray(new NameValuePair[0]);
			method.addParameters(parameters);
		}
	}
	class AlarmTimeComparator implements Comparator
	{

		long currentTime;
		public AlarmTimeComparator(long currentTime)
		{
			this.currentTime=currentTime;
		}
		public int compare(Object A, Object B) {
			// TODO Auto-generated method stub
			if((A==null)&&(B==null))
			{
				return 0;
			}else if((A!=null)&&(B==null))
			{
				return -1;
			}else if((A==null)&&(B!=null))
			{
				return 1;
			}else
			{
				Map.Entry a=(Map.Entry)A;
				Map.Entry b=(Map.Entry)B;
				long timeA=currentTime-((Long)a.getValue()).longValue();
				long timeB=currentTime-((Long)b.getValue()).longValue();
				
				if(timeA>timeB){
					return -1;
				}else if(timeA==timeB)
				{
					return 0;
				}else{
					return 1;
				}
	
			}
			
			
		}
		
		
	}
	public synchronized void removeOverdueAlarm() {
//		print content
//		Iterator it=alarmHistory.entrySet().iterator();
//		while(it.hasNext())
//		{
//			Map.Entry item=(Entry)it.next();
//			Long time=(Long)item.getValue();
//			logger.debug("vondart print alarmHistory time value before remove: "+time);
//		}
//		
		
//		add vondart
		
		synchronized(alarmHistory){
		Iterator it1=alarmHistory.entrySet().iterator();
		long currentTime=System.currentTimeMillis();
		long earliestTime=currentTime-CSGConfiguration.COMMON_ALARM_DISPATCH_HOLDTIME * 1000;
		logger.debug("earliest time is :"+earliestTime+", currentTime is:"+currentTime);
		while(it1.hasNext())
		{
			Map.Entry item=(Entry)it1.next();
			long time=((Long)item.getValue()).longValue();
			if(time<earliestTime)
			{
				it1.remove();
				logger.debug("removed entry:"+time);
			}else{
				break;
			}
		}
		}
		}

	public String getAsURL() {
		return asURL;
	}

	public void setAsURL(String asURL) {
		this.asURL = asURL;
	}
	
}
